<?php
class UsersController extends AppController {
	var $name = 'Users';
	var $uses = array('User', 'Response', 'Question', 'Survey');
	var $components = array('Email', 'PasswordHelper');
	
	/**
	 * User home page.
	 * 
	 * @return 
	 */
	function index() {
		// Set the name of the logged in user if there is any.
		$fields = $this->Auth->user();
		if(!empty($fields['User']['username'])) {
			$this->User->set('loggedIn', $fields['User']['username']);
		}
		if($fields['User']['group_id'] != 3) {
			$this->redirect(array('controller' => 'presenters', 'action' => 'index'));
		} else {
			$this->redirect(array('controller' => 'audiences', 'action' => 'index'));
		}
	}
	
	/**
	 * Register a user with the system.
	 * Also responsible for validating data entered into the form.
	 * 
	 * @return 
	 */
	function register() {
		// Pass the possible roles to the view.
		$this->set('roles', $this->User->Role->find('list'));
		
		// If POST data is not empty,
		if(!empty($this->data)) {
			
			// Set the user data.
			$this->User->set($this->data);
			
			// Validate password.
			if($this->data['User']['password'] === $this->Auth->password('')) {
				// Password field is blank, inform the user.
				// and validate the rest of the fields.
				$this->User->invalidate('password', 'Please enter a password');
			}
			
			// Do the same for password2.
			if($this->Auth->password($this->data['User']['password2']) === $this->Auth->password('')) {
				$this->User->invalidate('password2', 'Please enter a password');
			}
			
			// Make sure that the two passwords match
			if($this->Auth->password($this->data['User']['password2']) != $this->data['User']['password'])
			{
				$this->User->invalidate('password2', 'Passwords must match');
			}
			
			// Make sure "I agree to terms of service" is selected.
			if(empty($this->data['User']['terms'])) {
				$this->User->invalidate('terms', 'Please agree to terms');
			}			
			
			// If all fields are valid
			if($this->User->validates()) {
				
				// Prepare id for association with role.
				$this->data['Role']['user_id'] = $this->User->id;
				
				// If Presenter checkbox is checked, make user presenter.
				// Otherwise, make it audience member.
				if($this->data['User']['role'] == 1) {
					$this->data['User']['group_id'] = 1;
				} else {
					$this->data['User']['group_id'] = 3;
				}
				
				// Save the new user and redirect to the login page.
				$this->User->save($this->data, array('validate'=>'first'));
				$this->Session->setFlash('Registration successful, please login with your new account.', 'default', array(), 'register');
				$this->redirect(array('controller' => 'users', 'action' => 'login'));
			}
		}
	}
	
	/**
	 * Registers the active user for the given survey
	 * 
	 * @param object $survey_id [optional]
	 * @return 
	 */
	function registerForSurvey($survey_id = null) {
		$activeUser = $this->Auth->user();
		
		//Check if this user has registered for the survey already
		$this->User->bindModel(array('hasOne' => array('SurveysUser')));
		$userData =	$this->User->find('all', array(
				'fields' => array('User.*'),
				'conditions'=>array('SurveysUser.survey_id'=>$survey_id, 
									'SurveysUser.user_id'=>$activeUser['User']['id']), // id of survey
				'order'=>array('User.id ASC')
		));
		
		if(count($userData) == 1){
			$this->Session->setFlash("You've already registered for that survey");
			$this->redirect(array('controller' => 'audiences', 'action' => 'index'));
		}
		
		// Save the user
		else {
			$this->User->habtmAdd('Survey', $activeUser['User']['id'], $survey_id);
			$this->Session->setFlash('Thank you for registering!');
			$this->redirect(array('controller' => 'surveys', 'action' => 'user_view', '0' => $survey_id, '1' => 1));
		}
	}
	
	/**
	 * Displays the individual results view for the given survey and user
	 * 
	 * @param object $survey_id
	 * @param object $user_id
	 * @return 
	 */
	function displayIndividualResults($survey_id, $user_id){
		
		// find the user's name, to be passed to the view
		$users = $this->User->find('all', array( 'fields' => array ('User.firstname', 'User.lastname'), 
										'conditions' => array('User.id' => $user_id)));		
										
		$this->set('user_firstname', $users[0]['User']['firstname']);
		$this->set('user_lastname', $users[0]['User']['lastname']);
		
		// Makes sure there is one key for every survey user
		$this->Survey->Behaviors->attach('Containable');
		$this->Survey->bindModel(array('hasOne' => array('SurveysUser')));
		
		//Get all the surveys this user is registered for
		$my_surveys = $this->Survey->find('all',
			array(
				'conditions' => array('Survey.isDeleted' => 0, 'SurveysUser.user_id' => $user_id),
				'fields' => array('Survey.id', 'Survey.name'),
				'order' => array('Survey.created DESC'),
				'contain' => 'SurveysUser'
		));
		
		//Check to see if the user has actually registered for any surveys
		if(empty($my_surveys)) {
			$this->set('display', false);
		} else {
			$this->set('display', true);
			$options = array();
			
			foreach($my_surveys as $survey):
				$options[$survey['Survey']['id']] = $survey['Survey']['name'];
			endforeach;
			
			//Default to the first survey in the users list
			if($survey_id == 0){
				$survey_name = $my_surveys[0]['Survey']['name'];
				$survey_id = $my_surveys[0]['Survey']['id'];
			}
			
			$this->set('options', $options);
					
			$questionText = array();
			
			// 2d array of the Answers for each question
			// { {Question1 Answer1, Question1 Answer2}, {Question2 Answer1, Question 2 Answer2}... }
			$possibleResponses = array();
			
			// Selected Answer ids for each question
			$userResponse_ids = array();
	
			// Name of the survey, initially null								
			$survey_name;
									
			App::import('Model', 'QuestionsSurveys');						
			$this->Question->bindModel(array('hasOne' => array('QuestionsSurveys')));
			$questionsSurveys = $this->Question->find('all', array('fields' => array('*'), 'conditions' => array('QuestionsSurveys.survey_id' => $survey_id)));
			
			$questions_surveys_ids = array();
	
			// For each question in the survey
			foreach($questionsSurveys as $questionsSurvey){
				$survey = $questionsSurvey['Survey'];
				
				// Find answers to the questions in the correct order and make sure they are not deleted.
				$this->loadModel('Answer');
				$this->Answer->Behaviors->attach('Containable');
				$answers = $this->Answer->find('all', array(
					'conditions' => array(
						'Answer.question_id' => $questionsSurvey['Question']['id'],
						'Answer.isDeleted' => 0
					),
					'order' => array('Answer.viewOrder ASC')
				));
				
				for($i = 0; $i < count($answers); $i++) {
					$answers[$i] = $answers[$i]['Answer'];
				}
				
				// set the survey name, if it hasn't been set already
				if(! isset($survey_name)) $survey_name = $survey[0]['name'];
				
				// Grab the question text
				array_push($questionText, $questionsSurvey['Question']['text']);
		
				// Grab the possible Answers
				array_push($possibleResponses, $answers);
		
				// hang on to the questions_surveys id to lookup in the Response table
				array_push($questions_surveys_ids, $questionsSurvey['QuestionsSurveys']['id']);
			}
			
			// TODO: Possibly inefficient?
			// For each entry in the Response Table mapping a question in the survey to a user's response...
			foreach($questions_surveys_ids as $questions_surveys_id){
				
				// Find only the response from the desired user for the current question
				$users_response = $this->Response->find('all', array( 
								'fields' => array('Response.*'),
								'conditions' => array('Response.user_id'=>$user_id, 
														'Response.questions_surveys_id' => $questions_surveys_id)));
				if(count($users_response) > 0){													
				//TODO: We *should* be able to assume there is only one response. However, this is currently broken
				// due to ticket # 67, so we're arbitrarily picking the first response instead
					// Add the users' selected response to the set
					array_push($userResponse_ids, $users_response[0]['Response']['answer_id']);
				}else{
					// if the user hasn't responded, pass an invalid ID
					array_push($userResponse_ids, -1);
				}
			}
			
			$this->set('question_texts', $questionText);
			$this->set('possible_responses', $possibleResponses);
			$this->set('user_response_ids', $userResponse_ids);
			$this->set('survey_name', $survey_name);
			$this->set('survey_id', $survey_id);
			$this->set('user_id', $user_id);
		}
	}
	
	/**
	 * Select a survey to view from the dropdown menu and pass the variables to the view.
	 * 
	 * @return 
	 */
	function select_survey() {
		$this->redirect(array('action' => 'displayIndividualResults',
		 		$this->data['Survey']['id'], $this->data['User']['id']));
	}
	
	/**
	 * Called when the user reaches the login page or
	 * when the user tries to access a restricted page without being logged in.
	 * Auto-redirection needs to be set to false for this to work.
	 * 
	 * @return 
	 */
	function login() {
		// Check if the user is logged in.
		if($this->Auth->user()) {
			// Check if the user has just logged in and checked remember me.
			if(!empty($this->data) && $this->data['User']['rememberMe']) {
				// Store user information in the cookie and write it out.
				$cookie = array();
				$cookie['username'] = $this->data['User']['username'];
				$cookie['password'] = $this->data['User']['password'];
				$this->Cookie->write('Auth.User', $cookie, true, '2 weeks');
			}
			
			// Redirect user to the correct page.
			$this->redirect($this->Auth->redirect(array('controller' => 'users', 'action' => 'index')));
		}
		
		// If data is empty, the user was not redirected from the login page.
		if(empty($this->data)) {
			// Attempt to read the cookie and see if user has a remember me cookie.
			$cookie = $this->Cookie->read('Auth.User');
			if (!is_null($cookie)) {
				// Cookie is found, try to log user in.
				if ($this->Auth->login($cookie)) {
					// User logged in.
					// Clear auth message, just in case we use it.
					// Redirect user.
					$this->Session->del('Message.auth');
					$this->redirect($this->Auth->redirect(array('controller' => 'users', 'action' => 'index')));
				} else {
					// Failed to log user in.
					// Delete invalid Cookie to be safe.
					$this->Cookie->del('Auth.User');
				}
			}
		}
	}

	/**
	 * Called when the user logs out.
	 * 
	 * @return 
	 */
	function logout() {
		$this->Cookie->del('Auth.User');
		$this->Session->destroy();
		$this->redirect($this->Auth->logout());
	}
	
	/**
	 * Used to reset user passwords.
	 * 
	 * @return 
	 */
	function forgot_password() {
		$email = $this->data['User']['email'];
		// If User has submitted the form...
		if(!empty($this->data)) {
			// Make sure email field is not empty.
			if(empty($email)) {
				$this->User->invalidate('email', "We're sorry, we were unable to locate that email in our database. Please check your entry and try again.");
			} else {
				// Find all users associated with this email address.
				$this->User->Behaviors->attach('Containable');
				$foundUsers = $this->User->find('all', array(
					'conditions' => array('User.email' => $email),
					'contain' => false
				));

				// If users associated with this email are found,
				// Send a new generated password to them.
				if(!empty($foundUsers)) {
					foreach($foundUsers as $foundUser) {
						// Generate a random password with the length specified in $passwordLength.
						$passwordLength = 8;
						$generatedPassword = $this->PasswordHelper->generatePassword($passwordLength);
						
						// Update the user's password with the generated password.
						$this->User->read(NULL, $foundUser['User']['id']);
						$this->User->set('password', $this->Auth->password($generatedPassword));
						$this->User->save();
						
						// Send the email out.
						$this->Email->reset();
						$this->Email->from = 'CellSurv <cellsurv@cellsurv.com>';
						$this->Email->to = $foundUser['User']['email'];
						$this->Email->subject = 'Account Retrieval';
						$this->Email->replyTo = 'cellsurv@cellsurv.com';
						$this->Email->template = 'account_retrieval';
						$this->Email->sendAs = 'both';
						$this->set('User', $foundUser['User']);
						$this->set('newPassword', $generatedPassword);
						$this->Email->send();
					}
					// Redirect to confirmation page.
					$this->redirect(array('controller' => 'pages', 'action' => 'email_sent'));
				} else {
					// If email is not found, display the error.
					$this->User->invalidate('email', "We're sorry, we were unable to locate that email in our database. Please check your entry and try again.");
				}
			}
		}
	}
	
	/**
	 * Returns the contact us view
	 * 
	 * @return 
	 */
	function contact_us() {
		if(!empty($this->data)) {
			$this->render('thanks');
		} else {
			$this->render('contact_us');
		}
	}

	/**
	 * Called before the controller action.
	 * 
	 * @return 
	 */
    function beforeFilter() {
        //Configure AuthComponent
		parent::beforeFilter();
		$this->Auth->allowedActions = array('register', 'forgot_password', 'contact_us');
		$this->Auth->autoRedirect = false;
//		$this->_addGroups();
//		$this->_buildAcl();	// Uncomment this when you add new functions and run once and ONLY once.
//		$this->_initDB();
	}
	
	/**
	 * Used to add groups to the database.
	 * 
	 * @return 
	 */
	function _addGroups() {
		$this->loadModel('Group');
		$this->Group->set('name', 'Administrator');
		$this->Group->save();
		$this->Group->create();
		$this->loadModel('Group');
		$this->Group->set('name', 'Presenter');
		$this->Group->save();
		$this->Group->create();
		$this->Group->set('name', 'Audience');
		$this->Group->save();
	}

	
	/**
	 * Used to build the permissions table.
	 * 
	 * @return 
	 */
	function _initDB() {
	    $group =& $this->User->Group;
		
	    // Allow Admin to everything.
	    $group->id = 1; 
	    $this->Acl->allow($group, 'controllers');
	 
	    // Allow Presenters to CRUD surveys.
	    $group->id = 2;
	    $this->Acl->deny($group, 'controllers');
	    $this->Acl->allow($group, 'controllers/Users');
	    $this->Acl->allow($group, 'controllers/Surveys');
	 
	 	// Allow users access to only vheir portal and view/submit surveys.
	    $group->id = 3;
	    $this->Acl->deny($group, 'controllers');
		$this->Acl->allow($group, 'controllers/Users');
		$this->Acl->deny($group, 'controllers/Users/displayIndividualResults');
		$this->Acl->deny($group, 'controllers/Users/select_survey');
		$this->Acl->allow($group, 'controllers/Audiences');
		$this->Acl->allow($group, 'controllers/Surveys/user_view');
		$this->Acl->allow($group, 'controllers/Surveys/submit_answer');
		
	}
	
	/**
	 * Rebuild the Acl based on the current controllers in the application
	 *
	 * @return void
	 */
	function _buildAcl() {
    	$log = array();

		$aco =& $this->Acl->Aco;
        $root = $aco->node('controllers');
        if (!$root) {
            $aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
            $root = $aco->save();
            $root['Aco']['id'] = $aco->id; 
            $log[] = 'Created Aco node for controllers';
        } else {
            $root = $root[0];
        }
        
        App::import('Core', 'File');
        $Controllers = Configure::listObjects('controller');
        $appIndex = array_search('App', $Controllers);
        if ($appIndex !== false ) {
            unset($Controllers[$appIndex]);
        }
        $baseMethods = get_class_methods('Controller');
        $baseMethods[] = 'buildAcl';
        
        // look at each controller in app/controllers
        foreach ($Controllers as $ctrlName) {
            App::import('Controller', $ctrlName);
            $ctrlclass = $ctrlName . 'Controller';
            $methods = get_class_methods($ctrlclass);
            
            // find / make controller node
            $controllerNode = $aco->node('controllers/' . $ctrlName);
            if (!$controllerNode) {
                $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
                $controllerNode = $aco->save();
                $controllerNode['Aco']['id'] = $aco->id;
                $log[] = 'Created Aco node for ' . $ctrlName;
            } else {
                $controllerNode = $controllerNode[0];
            }
            
            // clean the methods. to remove those in Controller and private actions.
            foreach ($methods as $k => $method) {
                if (strpos($method, '_', 0) === 0) {
                    unset($methods[$k]);
                    continue;
                }
                if (in_array($method, $baseMethods)) {
                    unset($methods[$k]);
                    continue;
                }
                $methodNode = $aco->node('controllers/' . $ctrlName . '/' . $method);
                if (!$methodNode) {
                    $aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
                    $methodNode = $aco->save();
                    $log[] = 'Created Aco node for ' . $method;
                }
            }
        }
    }
}
?>